package com.dao;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.FactoryBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 这个类是MapperFactoryBean2的改进，MapperFactoryBean2的问题是不能反复调用
 * 因为第一次调用的时候sqlSession关闭，下次再调用的时候sqlSession关闭会导致调用不了
 * 这个类的改进就是在方法调用的时候才创建sqlSession，方法执行完毕之后就立即关上
 * <p>
 * 注意：这个实现还是不完善的，至少还没有考虑事务方面的内容，方法缓存等等，仅仅只是一个知识的演示
 */
public class MapperFactoryBean3 implements FactoryBean<Object> {
    private Class<?> mapperClass;
    private SqlSessionFactory factory;

    public SqlSessionFactory getFactory() {
        return factory;
    }

    public void setFactory(SqlSessionFactory factory) {
        this.factory = factory;
    }

    public Class<?> getMapperClass() {
        return mapperClass;
    }

    public void setMapperClass(Class<?> mapperClass) {
        this.mapperClass = mapperClass;
    }

    @Override
    public Object getObject() throws Exception {

        InvocationHandler invocationHandler = new MapperInvoker(getFactory(), getMapperClass());
        Object mapperProxy = Proxy.newProxyInstance(MapperFactoryBean3.class.getClassLoader(), new Class[]{getMapperClass()}, invocationHandler);
        return mapperProxy;
    }

    @Override
    public Class<?> getObjectType() {
        return mapperClass;
    }

    static class MapperInvoker implements InvocationHandler {
        private Class<?> mapperInterface;
        private SqlSessionFactory sqlSessionFactory;

        public MapperInvoker(SqlSessionFactory sqlSessionFactory, Class<?> mapperInterface) {
            System.out.println("传进来的sqlsessionFactory hashcode：" + sqlSessionFactory.hashCode());
            System.out.println("传进来的target hashcode：" + mapperInterface.hashCode());
            this.sqlSessionFactory = sqlSessionFactory;
            this.mapperInterface = mapperInterface;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
            String name = method.getName();
            System.out.println("---当前调用的方法名:" + name);

            SqlSession sqlSession = this.sqlSessionFactory.openSession(true);
            Object mapper = sqlSession.getMapper(this.mapperInterface);

            Object result = null;
            try {
                result = method.invoke(mapper, args);
            } finally {
                System.out.println("准备关闭sqlSession---hashcode:" + sqlSession.hashCode());
                sqlSession.close();
            }
            return result;
        }
    }
}
